﻿using UnityEngine;
using Lightbug.CharacterControllerPro.Core;
using Lightbug.Utilities;

namespace Lightbug.CharacterControllerPro.Demo
{

    public enum CameraTargetMode
    {
        Bounds,
        Point
    }

    [AddComponentMenu("Character Controller Pro/Demo/Camera/Camera 2D")]
    public class Camera2D : MonoBehaviour
    {
        [Header("Target")]

        [SerializeField] Transform target = null;

        [Header("Camera size")]

        [SerializeField]
        Vector2 cameraAABBSize = new Vector2(3, 4);

        [SerializeField]
        Vector2 targetAABBSize = new Vector2(1, 1);

        [Header("Position")]

        [SerializeField]
        CameraTargetMode targetMode = CameraTargetMode.Bounds;

        [SerializeField]
        Vector3 offset = new Vector3(0f, 0f, -10f);

        [SerializeField]
        float smoothTargetTime = 0.25f;

        [Header("Rotation")]

        [SerializeField]
        bool followRotation = true;

        [Min(0.1f)]
        [SerializeField]
        float rotationSlerpSpeed = 5f;



        [Header("Look ahead")]

        [Condition("targetMode", ConditionAttribute.ConditionType.IsEqualTo, ConditionAttribute.VisibilityType.Hidden, (int)CameraTargetMode.Bounds)]
        [SerializeField]
        float lookAheadSpeed = 4;

        [Condition("targetMode", ConditionAttribute.ConditionType.IsEqualTo, ConditionAttribute.VisibilityType.Hidden, (int)CameraTargetMode.Bounds)]
        [SerializeField]
        float xLookAheadAmount = 1;

        [Condition("targetMode", ConditionAttribute.ConditionType.IsEqualTo, ConditionAttribute.VisibilityType.Hidden, (int)CameraTargetMode.Bounds)]
        [SerializeField]
        float yLookAheadAmount = 1;

        // ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
        // ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
        // ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


        float xCurrentLookAheadAmount = 0;
        float yCurrentLookAheadAmount = 0;

        Vector3 targetCameraPosition;

        Vector3 smoothDampVelocity;
        Bounds cameraAABB;
        Bounds targetBounds;

        // Vector3 FinalTargetPosition => target.position + transform.TransformVector( offset );
        void Start()
        {

            if (target == null)
                Debug.Log("Missing camera target");

            Vector3 startingPosition = target.position;
            startingPosition.z = transform.position.z;
            transform.position = startingPosition;

            targetBounds = new Bounds(target.position, new Vector3(targetAABBSize.x, targetAABBSize.y, 1f));
            targetBounds.center = target.position;

            cameraAABB = new Bounds(target.position, new Vector3(cameraAABBSize.x, cameraAABBSize.y, 1f));

            targetCameraPosition = new Vector3(cameraAABB.center.x, cameraAABB.center.y, transform.position.z);
        }

        void OnDrawGizmos()
        {
            if (target == null)
                return;

            if (targetMode != CameraTargetMode.Bounds)
                return;

            Gizmos.color = new Color(0f, 0f, 1f, 0.2f);

            Bounds bounds = new Bounds(target.position, new Vector3(cameraAABBSize.x, cameraAABBSize.y, 1f));
            Gizmos.DrawCube(bounds.center, new Vector3(bounds.size.x, bounds.size.y, 1f));
        }

        void LateUpdate()
        {
            if (target == null)
                return;

            float dt = Time.deltaTime;

            UpdateTargetAABB();
            UpdateCameraAABB(dt);

            if (followRotation)
                UpdateRotation(dt);

            UpdatePosition(dt);
        }



        void UpdateTargetAABB()
        {
            targetBounds.center = target.position;
        }

        void UpdateCameraAABB(float dt)
        {
            float deltaLookAhead = lookAheadSpeed * dt;

            //X
            if (targetBounds.max.x > cameraAABB.max.x)
            {
                float deltaX = targetBounds.max.x - cameraAABB.max.x;
                cameraAABB.center += Vector3.right * deltaX;

                if (xCurrentLookAheadAmount < xLookAheadAmount)
                {
                    xCurrentLookAheadAmount += deltaLookAhead;
                    xCurrentLookAheadAmount = Mathf.Clamp(xCurrentLookAheadAmount, -xLookAheadAmount, xLookAheadAmount);
                }


            }
            else if (targetBounds.min.x < cameraAABB.min.x)
            {
                float deltaX = cameraAABB.min.x - targetBounds.min.x;
                cameraAABB.center -= Vector3.right * deltaX;

                //Look Ahead
                if (xCurrentLookAheadAmount > -xLookAheadAmount)
                {
                    xCurrentLookAheadAmount -= deltaLookAhead;
                    xCurrentLookAheadAmount = Mathf.Clamp(xCurrentLookAheadAmount, -xLookAheadAmount, xLookAheadAmount);
                }
            }

            //Y
            if (targetBounds.max.y > cameraAABB.max.y)
            {
                float deltaY = targetBounds.max.y - cameraAABB.max.y;
                cameraAABB.center += Vector3.up * deltaY;

                //Look Ahead
                if (yCurrentLookAheadAmount < yLookAheadAmount)
                {
                    yCurrentLookAheadAmount += deltaLookAhead;
                    yCurrentLookAheadAmount = Mathf.Clamp(yCurrentLookAheadAmount, -yLookAheadAmount, yLookAheadAmount);

                }


            }
            else if (targetBounds.min.y < cameraAABB.min.y)
            {

                float deltaY = cameraAABB.min.y - targetBounds.min.y;
                cameraAABB.center -= Vector3.up * deltaY;

                //Look Ahead
                if (yCurrentLookAheadAmount > -yLookAheadAmount)
                {
                    yCurrentLookAheadAmount -= deltaLookAhead;
                    yCurrentLookAheadAmount = Mathf.Clamp(yCurrentLookAheadAmount, -yLookAheadAmount, yLookAheadAmount);
                }
            }

            targetCameraPosition.x = cameraAABB.center.x + xCurrentLookAheadAmount;
            targetCameraPosition.y = cameraAABB.center.y + yCurrentLookAheadAmount;
        }


        void UpdatePosition(float dt)
        {
            Vector3 targetPos = Vector3.zero;
            if (targetMode == CameraTargetMode.Bounds)
            {
                targetPos = Vector3.SmoothDamp(transform.position, targetCameraPosition + transform.TransformVector(offset), ref smoothDampVelocity, smoothTargetTime);
            }
            else
            {
                targetPos = Vector3.SmoothDamp(transform.position, target.position + transform.TransformVector(offset), ref smoothDampVelocity, smoothTargetTime);
            }

            transform.position = targetPos;
        }

        void UpdateRotation(float dt)
        {
            Vector3 targetUp = Vector3.ProjectOnPlane(target.up, Vector3.forward);
            Quaternion deltaRotation = Quaternion.AngleAxis(Vector3.SignedAngle(transform.up, targetUp, Vector3.forward), Vector3.forward);
            transform.rotation *= Quaternion.Slerp(Quaternion.identity, deltaRotation, rotationSlerpSpeed * dt);
        }


    }

}
